package interpreter;

import java.io.*;
import java.util.*;

import exceptions.ExitDeadCodeException;
import parser.ParserWrapper;
import ast.Program;
import ast.AbstractValue;
import exceptions.DivByZeroException;
import exceptions.FailedStdinReadException;
import exceptions.UninitializedVariableException;

public class Interpreter {

    // Process return codes
    public static final int EXIT_SUCCESS = 0;
    public static final int EXIT_PARSING_ERROR = 1;
    // public static final int EXIT_STATIC_CHECKING_ERROR = 2;
    public static final int EXIT_UNINITIALIZED_VAR_ERROR = 3;
    public static final int EXIT_DIV_BY_ZERO_ERROR = 4;
    public static final int EXIT_FAILED_STDIN_READ = 5;
    public static final int EXIT_DEAD_CODE = 6;

    public static void main(String[] args) {
        String filename = args[0];
        Program astRoot = null;
        BufferedReader reader;
        try {
            reader = new BufferedReader(new FileReader(filename));
        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
        try {
            astRoot = ParserWrapper.parse(reader, filename);
        } catch (Exception ex) {
            Interpreter.fatalError("Uncaught parsing error: " + ex, EXIT_PARSING_ERROR);
        }
        // for debugging
        // astRoot.print(System.out);
        
        /* no checking for p3 */
        
        // // scanner test
        // Scanner scanner = new Scanner(System.in);
        // while (scanner.hasNextDouble()){
        //     System.out.println(scanner.nextDouble());
        // }
        // scanner.close();

        /* interpret */
        // create new scanner
        Scanner scanner = new Scanner(System.in);
        // create a empty state
        Stack<Map<String, AbstractValue>> stateStack= new Stack<Map<String, AbstractValue>>();
        stateStack.push(new HashMap<String, AbstractValue>());
        try {
            astRoot.interpret(stateStack, scanner);
        } catch (UninitializedVariableException ex) {
            // ex.printStackTrace();
            Interpreter.fatalError("Uninitialized variable: " + ex.getMessage(), EXIT_UNINITIALIZED_VAR_ERROR);
        } catch (DivByZeroException ex) {
            // ex.printStackTrace();
            Interpreter.fatalError("Division by zero", EXIT_DIV_BY_ZERO_ERROR);
        } catch (FailedStdinReadException ex) {
            // ex.printStackTrace();
            Interpreter.fatalError("Failed to read from stdin", EXIT_FAILED_STDIN_READ);
        } catch (ExitDeadCodeException ex) {
            // ex.printStackTrace();
            Interpreter.fatalError("Exit in dead code: " + ex.getMessage(), EXIT_DEAD_CODE);
        } catch (Exception ex) {
            // ex.printStackTrace();
            Interpreter.fatalError("Uncaught exception: " + ex, -1);
        }
        scanner.close();
        // System.out.println();
        System.exit(EXIT_SUCCESS);
    }

    public static void fatalError(String message, int processReturnCode) {
        System.out.println(message);
        System.exit(processReturnCode);
    }
}
